package com.yicheng.statistics.actor.analysis

import akka.actor.ActorLogging
import akka.actor.Props
import com.yicheng.statistics.actor.SaveHtaData
import akka.actor.Actor
import akka.actor.ActorRef
import com.yicheng.statistics.common.HTADB
import com.yicheng.statistics.common.RTAModel
import com.yicheng.statistics.common.RTAModel.CustomArea
import com.yicheng.statistics.common.TimeUtils
import com.yicheng.statistics.repo.cassandra.DataInfoDB
import scala.util.Failure
import scala.util.Success
import com.yicheng.statistics.common.GisMapUtil
import com.yicheng.statistics.common.GisPoint
import java.util.Date
import com.yicheng.statistics.repo.RTAModel.AreaInout
import com.yicheng.statistics.common.RTAModel.VehicleInOutAreaSetting
import org.joda.time.DateTime
import com.yicheng.statistics.service.HTAService
import com.yicheng.statistics.repo.model.Data.DataInfo
import com.carcloud.gateway.bean.data.DataVehPos
import com.carcloud.gateway.bean.data.DataVehOverall

/*
 * 进出区域统计分析
 * 在限制时间内 进入区域超过时间阀值 统计报警
 */
class AreaInOut extends Actor with ActorLogging{
  
  val saveHtaDataActor:ActorRef = context.child("saveHtaData").
    getOrElse(context.actorOf(Props[SaveHtaData], "saveHtaData"))
  
   import scala.concurrent.ExecutionContext.Implicits.global
  def receive: Receive = {
    case (deviceId:String, deviceType:Int, analyseGroupId:Long) =>
      log.info("进出区域 deviceId "+deviceId)
      HTAService.getOneDayDataInfo(deviceType, deviceId) onComplete { 
        case Success(dataInfos) =>
          HTADB.getVehicleInOutAreaSettings(analyseGroupId).map(s => 
            Parser(
              deviceId, 
              deviceType,
              s.headOption.get, 
              dataInfos.sortWith((d1:DataInfo, d2:DataInfo) => d1.data_time.getTime < d2.data_time.getTime)))
        case Failure(error) =>
          log.info(error.getStackTrace.toString())
      }
      //HTADB.getVehicleInOutAreaSettings(analyseGroupId).map(s => Parser(deviceId, deviceType, s.headOption.get) )
    case _ => log.info("AreaInOut")
  }
  
  /**
   * 
   */
  def Parser(deviceId:String ,deviceType:Int, setting:VehicleInOutAreaSetting, dataInfos:List[DataInfo]):Unit = {
    
    val area:CustomArea = setting.coverageArea.get
    val inAreaF = getInArea(area)
    
    var inState : InAreaState = InAreaState(false,None, None, None, None, None, None, None, None, None, None, None, None, None)
    var outState : OutAreaState = OutAreaState(false,None, None, None, None, None, None, None, None, None, None, None, None, None)
    
    dataInfos.map( di => {
      val posData: DataVehPos = di.data_vehicle.dataVehicle.get.posData.get
      val overAllData: DataVehOverall = di.data_vehicle.dataVehicle.get.overallData.get
      val lonlat = GisMapUtil.convert2baidu(posData.longitude.get, posData.latitude.get)//百度坐标系转换
      
      if( TimeUtils.inTimeRang(setting.startTime, setting.endTime, di.data_time)){//在需要计算的时间内
        val point = GisPoint(lonlat._2, lonlat._1)
        
        setting.alarmType match {
          case 0 =>//进区域报警
            if(inArea(point, area, inAreaF)){//在区域内
              if( inState.startTime.isEmpty){//进入区域第一条数据记录
                inState = inState.copy(
                    startTime = Some(di.data_time), 
                    startLon = Some(lonlat._1), 
                    startLat = Some(lonlat._2),
                    startMileage = Some(overAllData.currentMileage))
              }else{
                if( TimeUtils.inTimeRang(inState.startTime.get, di.data_time, setting.timeThreshold) ){//达到报警条件
                  
                  if(inState.alarm){//如果持续报警 计算报警的距离和位置
                    
                    inState = inState.copy(
                      endTime = Some(di.data_time),
                      endLon = Some(lonlat._1),
                      endLat = Some(lonlat._2),
                      alarmTime = Some(TimeUtils.secondsBetweenTime(inState.alarmStartTime.get, di.data_time)),
                      driveMileage = Some(overAllData.currentMileage - inState.startMileage.get))
                    
                  }else{
                    //记录报警的开始时间 地点 
                    inState = inState.copy(
                      alarm = true,
                      alarmStartTime = Some(di.data_time),
                      inTime = Some(TimeUtils.secondsBetweenTime(inState.startTime.get, di.data_time))
                    )
                    
                  }
                }
              }
              
            }else{
              if( !inState.startTime.isEmpty && inState.alarm){//有报警 说明出去了区域 记录报警结束的时间地点
                inState = inState.copy(
                    alarm = false,
                    endTime = Some(di.data_time),
                    endLon = Some(lonlat._1),
                    endLat = Some(lonlat._2),
                    alarmTime = Some(TimeUtils.secondsBetweenTime(inState.alarmStartTime.get, di.data_time)),
                    driveMileage = Some(overAllData.currentMileage - inState.startMileage.get)
                    )
                    
                val areaInout = AreaInout(
                      deviceType,
                      deviceId,
                      inState.startTime.get,
                      inState.endTime.get,
                      inState.inTime.get.toInt,
                      inState.startLon,
                      inState.startLat,
                      inState.endLon,
                      inState.endLat,
                      Some(area.id.toInt),
                      area.name,
                      Some(setting.analyseGroupsID.toInt),
                      setting.name,
                      DateTime.now().toDate(),
                      intomileage = inState.driveMileage,
                      None
                    )
                saveHtaDataActor ! areaInout
                
                //清理状态数据 进行下次分析
                inState = InAreaState(false,None, None, None, None, None, None, None, None, None, None, None, None, None)
              }
            }
            
            if( inState.alarm){//如果当天最后一次还在报警 保存报警的
              val areaInout = AreaInout(
                      deviceType,
                      deviceId,
                      inState.startTime.get,
                      inState.endTime.get,
                      inState.inTime.get.toInt,
                      inState.startLon,
                      inState.startLat,
                      inState.endLon,
                      inState.endLat,
                      Some(area.id.toInt),
                      area.name,
                      Some(setting.analyseGroupsID.toInt),
                      setting.name,
                      DateTime.now().toDate(),
                      intomileage = inState.driveMileage,
                      None
                    )
                saveHtaDataActor ! areaInout
            }
          case 1 =>
            if( inArea(point,area, inAreaF)){//如果在区域内
              if( !outState.startTime.isEmpty && outState.alarm){//有报警 说明回到区域 记录报警结束的时间地点
                outState = outState.copy(
                    alarm = false,
                    endTime = Some(di.data_time),
                    endLon = Some(lonlat._1),
                    endLat = Some(lonlat._2),
                    alarmTime = Some(TimeUtils.secondsBetweenTime(outState.alarmStartTime.get, di.data_time)),
                    driveMileage = Some(overAllData.currentMileage - outState.startMileage.get)
                    )
                    
                val areaInout = AreaInout(
                      deviceType,
                      deviceId,
                      inState.startTime.get,
                      inState.endTime.get,
                      inState.inTime.get.toInt,
                      inState.startLon,
                      inState.startLat,
                      inState.endLon,
                      inState.endLat,
                      Some(area.id.toInt),
                      area.name,
                      Some(setting.analyseGroupsID.toInt),
                      setting.name,
                      DateTime.now().toDate(),
                      intomileage = inState.driveMileage,
                      None
                    )
                saveHtaDataActor ! areaInout
                
                //清理状态数据 进行下次分析
                inState = InAreaState(false,None, None, None, None, None, None, None, None, None, None, None, None, None)
              }
            }else{//不在区域内
              if( outState.startTime.isEmpty){ //记录第一条离开的时间 地点 和里程
                outState = outState.copy(
                    startTime = Some(di.data_time), 
                    startLon = Some(lonlat._1), 
                    startLat = Some(lonlat._2), 
                    startMileage = Some(overAllData.currentMileage))
              }else{
                if( TimeUtils.secondsBetweenTime(outState.startTime.get, di.data_time) > setting.timeThreshold){//超过时间门限 开始记录报警
                  if(outState.alarm){//如果持续报警 计算报警的距离和位置
                    
                    outState = outState.copy(
                      endTime = Some(di.data_time),
                      endLon = Some(lonlat._1),
                      endLat = Some(lonlat._2),
                      alarmTime = Some(TimeUtils.secondsBetweenTime(inState.alarmStartTime.get, di.data_time)),
                      driveMileage = Some(overAllData.currentMileage - inState.startMileage.get))
                    
                  }else{
                    //记录报警的开始时间 地点 
                    outState = outState.copy(
                      alarm = true,
                      alarmStartTime = Some(di.data_time),
                      outTime = Some(TimeUtils.secondsBetweenTime(outState.startTime.get, di.data_time))
                    )
                    
                  }
                }
              }
            }
            
            if( outState.alarm){//如果当天最后一次还在报警 保存报警的
              val areaInout = AreaInout(
                      deviceType,
                      deviceId,
                      outState.startTime.get,
                      outState.endTime.get,
                      outState.outTime.get.toInt,
                      outState.startLon,
                      outState.startLat,
                      outState.endLon,
                      outState.endLat,
                      Some(area.id.toInt),
                      area.name,
                      Some(setting.analyseGroupsID.toInt),
                      setting.name,
                      DateTime.now().toDate(),
                      None,
                      outmileage = outState.driveMileage
                    )
                saveHtaDataActor ! areaInout
            }
          case 2 => //进出区域都要报警
            if( inArea(point,area, inAreaF)){//如果在区域内
              if( inState.startTime.isEmpty){//进入区域第一条数据记录
                inState = inState.copy(
                    startTime = Some(di.data_time), 
                    startLon = Some(lonlat._1), 
                    startLat = Some(lonlat._2),
                    startMileage = Some(overAllData.currentMileage))
              }else{
                if( TimeUtils.inTimeRang(inState.startTime.get, di.data_time, setting.timeThreshold) ){//达到报警条件
                  
                  if(inState.alarm){//如果持续报警 计算报警的距离和位置
                    
                    inState = inState.copy(
                      endTime = Some(di.data_time),
                      endLon = Some(lonlat._1),
                      endLat = Some(lonlat._2),
                      alarmTime = Some(TimeUtils.secondsBetweenTime(inState.alarmStartTime.get, di.data_time)),
                      driveMileage = Some(overAllData.currentMileage - inState.startMileage.get))
                    
                  }else{
                    //记录报警的开始时间 地点 
                    inState = inState.copy(
                      alarm = true,
                      alarmStartTime = Some(di.data_time),
                      inTime = Some(TimeUtils.secondsBetweenTime(inState.startTime.get, di.data_time))
                    )
                    
                  }
                }
              }
              
              //###################################################
              
              if( !outState.startTime.isEmpty && outState.alarm){//有报警 说明回到区域 记录报警结束的时间地点
                outState = outState.copy(
                    alarm = false,
                    endTime = Some(di.data_time),
                    endLon = Some(lonlat._1),
                    endLat = Some(lonlat._2),
                    alarmTime = Some(TimeUtils.secondsBetweenTime(outState.alarmStartTime.get, di.data_time)),
                    driveMileage = Some(overAllData.currentMileage - outState.startMileage.get)
                    )
                    
                val areaInout = AreaInout(
                      deviceType,
                      deviceId,
                      inState.startTime.get,
                      inState.endTime.get,
                      inState.inTime.get.toInt,
                      inState.startLon,
                      inState.startLat,
                      inState.endLon,
                      inState.endLat,
                      Some(area.id.toInt),
                      area.name,
                      Some(setting.analyseGroupsID.toInt),
                      setting.name,
                      DateTime.now().toDate(),
                      intomileage = inState.driveMileage,
                      None
                    )
                saveHtaDataActor ! areaInout
                
                //清理状态数据 进行下次分析
                inState = InAreaState(false,None, None, None, None, None, None, None, None, None, None, None, None, None)
              }
            }else{//不在区域内
              if( outState.startTime.isEmpty){ //记录第一条离开的时间 地点 和里程
                outState = outState.copy(
                    startTime = Some(di.data_time), 
                    startLon = Some(lonlat._1), 
                    startLat = Some(lonlat._2), 
                    startMileage = Some(overAllData.currentMileage))
              }else{
                if( TimeUtils.secondsBetweenTime(outState.startTime.get, di.data_time) > setting.timeThreshold){//超过时间门限 开始记录报警
                  if(outState.alarm){//如果持续报警 计算报警的距离和位置
                    
                    outState = outState.copy(
                      endTime = Some(di.data_time),
                      endLon = Some(lonlat._1),
                      endLat = Some(lonlat._2),
                      alarmTime = Some(TimeUtils.secondsBetweenTime(inState.alarmStartTime.get, di.data_time)),
                      driveMileage = Some(overAllData.currentMileage - inState.startMileage.get))
                    
                  }else{
                    //记录报警的开始时间 地点 
                    outState = outState.copy(
                      alarm = true,
                      alarmStartTime = Some(di.data_time),
                      outTime = Some(TimeUtils.secondsBetweenTime(outState.startTime.get, di.data_time))
                    )
                    
                  }
                }
              }
              
              //###################################
              
              if( outState.startTime.isEmpty){ //记录第一条离开的时间 地点 和里程
                outState = outState.copy(
                    startTime = Some(di.data_time), 
                    startLon = Some(lonlat._1), 
                    startLat = Some(lonlat._2), 
                    startMileage = Some(overAllData.currentMileage))
              }else{
                if( TimeUtils.secondsBetweenTime(outState.startTime.get, di.data_time) > setting.timeThreshold){//超过时间门限 开始记录报警
                  if(outState.alarm){//如果持续报警 计算报警的距离和位置
                    
                    outState = outState.copy(
                      endTime = Some(di.data_time),
                      endLon = Some(lonlat._1),
                      endLat = Some(lonlat._2),
                      alarmTime = Some(TimeUtils.secondsBetweenTime(inState.alarmStartTime.get, di.data_time)),
                      driveMileage = Some(overAllData.currentMileage - inState.startMileage.get))
                    
                  }else{
                    //记录报警的开始时间 地点 
                    outState = outState.copy(
                      alarm = true,
                      alarmStartTime = Some(di.data_time),
                      outTime = Some(TimeUtils.secondsBetweenTime(outState.startTime.get, di.data_time))
                    )
                    
                  }
                }
              }
            }
          if( inState.alarm){//如果当天最后一次还在报警 保存报警的
            val areaInout = AreaInout(
                    deviceType,
                    deviceId,
                    inState.startTime.get,
                    inState.endTime.get,
                    inState.inTime.get.toInt,
                    inState.startLon,
                    inState.startLat,
                    inState.endLon,
                    inState.endLat,
                    Some(area.id.toInt),
                    area.name,
                    Some(setting.analyseGroupsID.toInt),
                    setting.name,
                    DateTime.now().toDate(),
                    intomileage = inState.driveMileage,
                    None
                  )
              saveHtaDataActor ! areaInout
          }
          
          //############################################################
          if( outState.alarm){//如果当天最后一次还在报警 保存报警的
            val areaInout = AreaInout(
                    deviceType,
                    deviceId,
                    outState.startTime.get,
                    outState.endTime.get,
                    outState.outTime.get.toInt,
                    outState.startLon,
                    outState.startLat,
                    outState.endLon,
                    outState.endLat,
                    Some(area.id.toInt),
                    area.name,
                    Some(setting.analyseGroupsID.toInt),
                    setting.name,
                    DateTime.now().toDate(),
                    None,
                    outmileage = outState.driveMileage
                  )
              saveHtaDataActor ! areaInout
          }
          
        }
      }
    })
  }
  
  def inArea(p:GisPoint,area:CustomArea, f: (GisPoint, CustomArea) => Boolean):Boolean = {
    f(p, area)
  }
  
  def inCircelArea(p:GisPoint, area:CustomArea):Boolean = {
    val radius:Float = area.radius.get
    val point = GisPoint(area.lat.get.toDouble, area.lon.get.toDouble)
    GisMapUtil.IsPointInCircle(point, List(p), radius)
  }
  
  def inRectArea(p:GisPoint, area:CustomArea):Boolean = {
    
    val gisPoints:List[GisPoint] = area.originlnglat.get.split(",").map{ x =>
      val point = x.split("\\|")
      GisPoint(point.head.toDouble, point.last.toDouble)
    }.toList
    
    GisMapUtil.IsPointInRegion(p, gisPoints)
  }
  
  def inPolygonArea(p:GisPoint, area:CustomArea):Boolean = {
    val gisPoints:List[GisPoint] = area.originlnglat.get.split(",").map{ x =>
      val point = x.split("\\|")
      GisPoint(point.head.toDouble, point.last.toDouble)
    }.toList
    
    GisMapUtil.IsPointInRegion(p, gisPoints)
  }
  
  def getInArea(area:CustomArea):(GisPoint, CustomArea) => Boolean = {
    val fun = area.drawingType.get match {
      case "1" => inCircelArea _ 
      case "2" => inRectArea _
      case "3" => inPolygonArea _
    }
    fun
  }
}

case class InAreaState(
    alarm:Boolean,
    startTime:Option[Date],//进入区域时间
    endTime:Option[Date],//离开区域时间
    inTime:Option[Long],//在区域内时间
    alarmStartTime:Option[Date],//报警开始时间
    alarmEndTime:Option[Date],//报警结束时间
    alarmTime:Option[Long],//持续报警时间
    startLon:Option[Double],//进入精度
    startLat:Option[Double],//进入纬度
    endLon:Option[Double],
    endLat:Option[Double],
    startMileage:Option[Int],
    endMileage:Option[Int],
    driveMileage:Option[Int]
    )
case class OutAreaState(
    alarm:Boolean,
    startTime:Option[Date],//离开区域时间
    endTime:Option[Date],//进入区域时间
    outTime:Option[Long],//在区域内时间
    alarmStartTime:Option[Date],//报警开始时间
    alarmEndTime:Option[Date],//报警结束时间
    alarmTime:Option[Long],//持续报警时间
    startLon:Option[Double],//出区域精度
    startLat:Option[Double],//出区域纬度
    endLon:Option[Double],
    endLat:Option[Double],
    startMileage:Option[Int],
    endMileage:Option[Int],
    driveMileage:Option[Int]
)